/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package csm.uttils.generics;

import java.io.Serializable;

/**
 *
 * @author Carlos S. Moreno
 */
public class Roster < N extends Comparable> implements Serializable {

    private Node<N> first, actual;

    public Roster()
    {
        first = actual = null;
    }
    /**
     * Add the object in the first place of the Roster
     * @param info Object to add
     */
    public void addFirst(N info)
    {
        if (info != null)
        {
            first = new Node<N>( info, first);
        }
    }//Fin addFirst
/**
 * Add an object in keeping the roster sorted.
 * @param info Object to add
 */
    public void addInOrder(N info)
    {
        if (info != null)
        {
            Node<N> nvo = new Node<N>( info, null);
            Node<N> p = first, q = null;

            /*
             * El bucle se corta al llegar al último elemento de la lista
             * o al encontrar el lugar en el que debe ser insertado el
             * nuevo objeto.
             */
            while (p != null && info.compareTo(p.getInfo()) >= 0)
            {
                q = p;
                p = p.getNext();
            }

            nvo.setNext(p);

            if (q != null) q.setNext(nvo);
            else first = nvo;//Si aun no hay nada en la lista.

        }
    }//Fin addInOrder

    /**
     * Add an object in the last place of the roster.
     * @param info Object to add
     */
    public void addLast(N info)
    {
        if (first == null) first=new Node<N>(info, null);
        else goLast().setNext(new Node<N>(info, null));


    }//Fin addLast

    /**
     * Return the first object of the roster
     * @return the first object
     * @exception NoSuchElementException If the roster is empty
     */
    public N getFirst()
    {
        if (first == null) throw new java.util.NoSuchElementException("No hay elementos en la lista");

         return  (N) first.getInfo();
    }//Fin getFirst

    /**
     * Return the last object of the roster.
     * @return the last object
     * @exception NoSuchElementException If the roster is empty
     */
    public N getLast()
    {
        if (first == null) throw new java.util.NoSuchElementException("No hay elementos en la lista");

        return (N) goLast().getInfo();

    }//Fin getLast

    public N getElementInPosition(int x)
    {
        if (first == null) {
            throw new java.util.NoSuchElementException("No hay elementos en la lista");
        }

        N result = null;
        if (0 <= x && x < count()) {

            Node<N> p = first, q = null;
            int index = 0;
            while (p != null && index != x) {
                p = p.getNext();
                index++;
            }
            result = p.getInfo();
        }

        return result;
    }

    /**
     * Returns the index in the roster of the given element. If the roster
     * is empty, or the element it doesn't exist, it return -1.
     * @param info
     * @return
     */
    public int indexOf(N info)
    {
        int index = -1;
        if (first == null) {
            throw new java.util.NoSuchElementException("The roster is empty");
        }

        Node<N> p = first, q = null;
        int aux = 0;
        boolean continueSearching = true;
        while (p != null && continueSearching) {
            if (p.getInfo().compareTo(info) == 0) {
                index = aux;
                continueSearching = false;
            } else {
                aux++;
                p = p.getNext();
            }
        }

        return index;
    }

    /**
     * return the class of the content objects in the roster.
     * @return Roster obects class
     * @exception NoSuchElementException If the roster is empty
     */
    public Class getElementsClass()
    {
        if (first == null) throw new java.util.NoSuchElementException("The roster is empty");
        return first.getInfo().getClass();
    }//Fin getElementsClass

    /**
     * Return the amount of elements saved on the roster.
     * @return Amount of elements
     */
    public int count()
    {
        if (first == null) return 0;

        Node<N> p = first;
        int contador = 0;
        while(p != null)//Recorro toda la lista
        {
            contador ++;
            p = p.getNext();
        }
        return contador;
    }

     /**
     * Indicates if the roster is empty
     * @return Verdadero: Si la Roster tiene elementos cargados - Falso: Si la Roster está vacia
     */
    public boolean isEmpty()
    {
        return (first == null);
    }//Fin isEmpty


    /**
     * Erase all the elements on the roster.
     */
    public void clear()
    {
        first = null;
    }//Fin clear

    
    /**
     * Indicates if there are more elementes on the roster
     * @return True if there are more element, False if there are not.
     */
    public boolean hasNext()
    {
        //Si no hay elementos en la lista, devuelve falso
        if (first == null) return false;

        //Si el elemento actual, no es nulo, pero no tiene elemento alguno
        //  referenciado en next, devuelve false
        if (actual != null && actual.getNext() == null) return false;

        //Si ninguna de las condiciones anteriores de cumplen, entonces la
        //  lista efectivamente tiene un proximo elemento, entonces...
        return true;
    }//Fin hasNext


    /**
     * Returns the next element on the roster, and moves the cursor forward.
     * @return next element
     * @exception NoSuchElementException If the roster is empty
     */
    public N next()
    {
        //Si no hay mas elementos a continuación, lanza una excepción
        if (! hasNext())  throw new java.util.NoSuchElementException("No hay mas elementos en la Lista");

        if (actual != null) actual = actual.getNext();
        else actual = first;

        return (N) actual.getInfo();

    }//Fin next


    /**
     * Erase a specific element on the roster, and return it.
     * @param info element to erase
     * @return Erased element
     */
    public N remove(N info)
    {
        if (first == null) throw new java.util.NoSuchElementException("No hay elementos en la lista");

        this.startIterator();
        Node<N> p, q;
        p = q = first;
        int contador = 0;
        while(p != null)//Recorro toda la lista
        {
            if (p.getInfo().compareTo(info)==0)
            {
              if (contador == 0)  first = first.getNext();
              q.setNext(p.getNext());
              return p.getInfo();
            }
            contador ++;
            q = p;
            p = p.getNext();
        }
        return null;
    }

    /**
     * Return the first element, and erase it from the roster.
     * @return First element on the roster
     * @exception NoSuchElementException if the roster is empty
     */
    public N removeFirst()
    {
        if (first == null) throw new java.util.NoSuchElementException(
                "The roster is empty");

        N firstInfo = (N)first.getInfo();

        if (first.getNext() == null) first = null;
        else first = first.getNext();

        return firstInfo;
    }//Fin removeFirst

    /**
     * Return the last element, and erase it from the roster.
     * @return Last element on the roster
     * @exception NoSuchElementException if the roster is empty
     */
    public N removeLast()
    {
        if (first == null) throw new java.util.NoSuchElementException(
                "The roster is empy");

        Node<N> penultimate = goLastButOne();

        if (count() ==1){
            first = null;
            return penultimate.getInfo();
        }
        N last = (N)penultimate.getNext().getInfo();
        penultimate.setNext(null);

        return last;

    }//Fin removeLast

    /**
     * Return the element.
     * @return Last element
     */
    private Node<N> goLast()
    {
        Node<N> p = first, q =null;

        while (p!=null)
        {
            q = p;
            p = p.getNext();
        }

        //Si la lista tiene un solo elemento devuelve el Objeto del 'frente'
        return (q != null)?q:first;

    }//Fin goLast

    /**
     * Return the penultimate element on the roster.
     * @return Penultimate element
     */
    private Node<N> goLastButOne()
    {
        Node <N> p = first, q = null;

        while( p.getNext() != null )
         {
            q = p;
            p = p.getNext();
         }

        if (q != null) return q;
        else return first;
    }//Fin goLastButOne

    /**
     *  The next time the "next" method is call, it will return the first
     * element on the roster.
     */
    public void startIterator()
    {
        actual = null;
    }//Fin startIterator

    
    /**
     * Returns an Array<> with the elements of the roster
     * @return ArrayList<N>
     */
    public Comparable[] toArray()
    {
        startIterator();
        Comparable[] ar=new Comparable[count()];
        int i=0;
        while(hasNext())
        {
           ar[i]=next();
           i++;
        }
        return ar;
    }//Fin 

    
    /**
     * Fill the roster with the objects of an array.
     * Any previous data on the roster will be deleted.
     * @param array Data to fill the roster with.
     */
    public void getDataFromArray(N[] array)
    {
        clear();
        for ( N info : array)
        {
            this.addInOrder(info);
        }
    }

    
    @Override
    public String toString()
    {
        Node<N> p = first;
        String result = "Lista: ";

        while (p!= null)
        {
            result += p.toString() + ", ";
            p = p.getNext();
        }
        
        return result;

    }//Fin toString

}//Fin Roster<N>
